package com.whz.generic.api.exception;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.whz.generic.message.GenericMessageAccessor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;

/**
 * 同余自定义异常基类
 * <p>generic-message.properties中定义 "消息key=value"</p>
 *
 * @author whz
 */
@Data
@JsonIgnoreProperties({ "stackTrace", "localizedMessage", "suppressed", "messageFileName" })
@EqualsAndHashCode(callSuper = true)
public final class CommonBusinessException extends RuntimeException {
    public static final int SERVICE_INTERNAL_ERROR_CODE = 500;
    public static final String SERVICE_INTERNAL_ERROR = "service internal error.";
    private static final long serialVersionUID = 3376750275411354932L;


    @JsonIgnore
    private String messageFileName;

    /**
     * 配置在message.properties的消息的key
     */
    @JsonIgnore
    private String propertyKey;

    /**
     * 带{0}占位的消息
     */
    @JsonIgnore
    private Object[] placeHolderArgs;

    /**
     * 错误码,复用 http status
     */
    @JsonProperty("code")
    private int code;

    /**
     * 返回的异常消息
     */
    @JsonIgnore
    private String message;

    @JsonIgnore
    private Locale locale;

    /**
     * 默认配置的业务异常,500:service internal error.
     */
    private CommonBusinessException() {
        code = SERVICE_INTERNAL_ERROR_CODE;
        message = SERVICE_INTERNAL_ERROR;
    }

    @JsonProperty("error")
    @Override
    public String getMessage() {
        return message;
    }


    private void bindMessage() {
        if (!StringUtils.isEmpty(propertyKey)) {
            if (ObjectUtils.isEmpty(placeHolderArgs)) {
                message = bundle().getMessage(propertyKey);
            } else {
                message = bundle().getMessage(propertyKey, placeHolderArgs);
            }
        }
        if (!StringUtils.isEmpty(message)) {
            message = SERVICE_INTERNAL_ERROR;
        }
    }

    /**
     * 异常类的性能问题就是跟踪当前线程栈帧
     *
     * @return 当前异常
     */
    @Override
    public synchronized Throwable fillInStackTrace() {
        return this;
    }


    /**
     * ResourceBundle bundle = ResourceBundle.getBundle("res", new Locale("zh", "CN"));
     * 其中new Locale(“zh”, “CN”)提供本地化信息，上面这行代码，程序会首先在classpath下寻找my_zh_CN.properties文件，若my_zh_CN.properties文件不存在，则取找my_zh.properties，如还是不存在，继续寻找my.properties,若都找不到就抛出异常。
     */
    private MessageSourceAccessor bundle() {
        return GenericMessageAccessor.getAccessor(messageFileName);
    }


    public static class Builder {

        private CommonBusinessException target;

        public Builder create() {
            target = new CommonBusinessException();
            return this;
        }

        public Builder code(int businessCode) {
            target.code = businessCode;
            return this;
        }

        public Builder messageFileName(String messageFileName) {
            Assert.hasText(messageFileName, "messageFileName MUST NOT be blank.");
            target.messageFileName = messageFileName;
            return this;
        }


        public Builder propertyKey(String propertyKey) {
            Assert.hasText(propertyKey, "propertyKey MUST NOT be blank.");
            target.propertyKey = propertyKey;
            return this;
        }

        public Builder placeHolderArgs(Objects... placeHolderArgs) {
            Assert.noNullElements(placeHolderArgs, "placeHolderArgs MUST NOT be blank.");
            target.placeHolderArgs = Arrays.copyOf(placeHolderArgs, placeHolderArgs.length);
            return this;
        }

        public CommonBusinessException build() {
            target.bindMessage();
            return target;
        }
    }
}